package jwt

import (
	"context"
	"github.com/go-kratos/kratos/v2/errors"
	"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
	jwtv4 "github.com/golang-jwt/jwt/v4"
	"strconv"
	"time"
)

/*
	1、将用户账号或者id加密到jwt载荷中，生成access_token
	2、从jwt解析数据出来，如userid等
*/

// GenerateJwtTokenByMapClaims 将用户ID加密到jwt载荷生成token
// 自定义jwt token的claim声明
func GenerateJwtTokenByMapClaims(key []byte, userId int64) (string, error) {
	claims := jwtv4.NewWithClaims(
		jwtv4.SigningMethodHS256,
		jwtv4.MapClaims{
			"user_id":      userId,
			"authority_id": userId,
			"time_stamp":   time.Now().Unix(),
		},
	)
	return claims.SignedString(key)
}

// ExtractUserIDFromClaims 从token中解析出用户id
func ExtractUserIDFromClaims(ctx context.Context, c ...jwtv4.Claims) int64 {
	var claims jwtv4.Claims
	if len(c) > 0 {
		claims = c[0]
	} else {
		var ok bool
		claims, ok = jwt.FromContext(ctx)
		if !ok {
			return 0
		}
	}

	str, ok := claims.(jwtv4.MapClaims)["user_id"]
	if !ok || str == nil {
		return 0
	}

	switch str.(type) {
	case string:
		userId, err := strconv.ParseInt(str.(string), 10, 64)
		if err != nil {
			return 0
		}
		return userId
	case float64:
		return int64(str.(float64))
	default:
		return 0
	}
}

// ExtractFromClaims 从token中提取出MapClaims信息
func ExtractFromClaims(jwtToken string, key []byte) (jwtv4.MapClaims, error) {
	//解析token里面的信息
	tokenInfo, err := jwtv4.Parse(jwtToken, func(token *jwtv4.Token) (interface{}, error) {
		return key, nil
	})
	if err != nil {
		ve, ok := err.(*jwtv4.ValidationError)
		if !ok {
			return nil, errors.Unauthorized("UNAUTHORIZED", err.Error())
		}
		//格式不正确
		if ve.Errors&jwtv4.ValidationErrorMalformed != 0 {
			return nil, jwt.ErrTokenInvalid
		}
		//过期
		if ve.Errors&jwtv4.ValidationErrorExpired|jwtv4.ValidationErrorNotValidYet != 0 {
			return nil, jwt.ErrTokenExpired
		}
		return nil, jwt.ErrTokenParseFail
	}

	if !tokenInfo.Valid {
		return nil, jwt.ErrTokenParseFail
	}

	if tokenInfo.Method != jwtv4.SigningMethodES256 {
		return nil, jwt.ErrUnSupportSigningMethod
	}

	claims := tokenInfo.Claims
	return claims.(jwtv4.MapClaims), nil
}
